import sys
import os.path
import signal
import time
import socket
import logging
import shutil
from multiprocessing import Process
import daemonize


LOG = logging.getLogger(__name__)


CONFIG_FILEPATH = 'tests/demo/conf/test_config.toml'
OUTPUT_FILEPATH = 'tests/demo/logs/demo_server.log'
PID_FILEPATH = 'tests/demo/logs/demo_server.pid'
DEMO_TEMP_DIR = 'tests/demo/temp'
DEMO_LOGS_DIR = 'tests/demo/logs'
DEMO_DATA_DIR = 'tests/demo/data'


def demo_server_main():
    with open(PID_FILEPATH, 'w') as f:
        f.write(str(os.getpid()))
    # change stdout and stderr to output file
    output = open(OUTPUT_FILEPATH, 'w')
    for fd in [1, 2]:
        os.close(fd)
        os.dup2(output.fileno(), fd)
    environ = dict(os.environ)
    environ.update({'TINYOPS_CONFIG': CONFIG_FILEPATH})
    command = [sys.executable, '-m', 'tinyops.main', 'start']
    os.execvpe(sys.executable, command, environ)


def demo_server_daemon():
    demo_server = daemonize.Daemonize(
        app='demo_server',
        pid=PID_FILEPATH,
        action=demo_server_main,
        chdir=os.getcwd(),
    )
    demo_server.start()


class DemoServer:
    def __init__(self):
        self.url_prefix = 'http://127.0.0.1:9010/tinyops'
        self.webhook_url_prefix = 'http://127.0.0.1:9011/tinyops'
        self.pid = None

    def read_output(self):
        with open(OUTPUT_FILEPATH) as f:
            return f.read()

    def _read_pid(self):
        with open(PID_FILEPATH) as f:
            value = f.read()
            if value:
                return int(value)
        return None

    def _clean_demo_directory(self):
        for d in [DEMO_TEMP_DIR, DEMO_LOGS_DIR, DEMO_DATA_DIR]:
            if os.path.exists(d):
                shutil.rmtree(d)

    def start(self, timeout=5):
        LOG.info('start demo server for test')
        self._clean_demo_directory()
        os.makedirs(os.path.dirname(OUTPUT_FILEPATH), exist_ok=True)
        p = Process(target=demo_server_daemon)
        p.start()
        p.join()
        deadline = time.time() + timeout
        while time.time() < deadline:
            self.pid = self._read_pid()
            if self.pid is not None:
                break
        assert self.pid is not None, f'daemon server not start in {timeout} seconds'

    def stop(self, timeout=5):
        LOG.info('stop demo server for test')
        if self.pid is not None:
            exited = False
            deadline = time.time() + timeout
            while time.time() < deadline:
                try:
                    os.kill(self.pid, signal.SIGINT)
                except OSError:
                    exited = True
                    break
                time.sleep(1)
            assert exited, f'daemon server not stop in {timeout} seconds'

    def url_for(self, path):
        return self.url_prefix + '/' + path.lstrip('/')

    def webhook_url_for(self, path):
        return self.webhook_url_prefix + '/' + path.lstrip('/')

    def _is_ready(self):
        try:
            sock = socket.create_connection(('127.0.0.1', 9010), timeout=0.1)
            sock.close()
        except (ConnectionError, IOError):
            return False
        else:
            return True

    def wait_ready(self, timeout=5):
        deadline = time.time() + timeout
        while time.time() < deadline:
            if self._is_ready():
                return
            time.sleep(0.1)
        assert False, f'demo server not ready in {timeout} seconds'
